#include "flash_memory.h"
#include "spi.h"
#include "c8051f3xx.h"

char * writeBytesSource;
char writeBytesRemaining;

char * readBytesDestination;
char readBytesRemaining;

// 
void spi_flash_memory_enable(void)
{
	// Wait until there is not SPI transfer in progress
	while ((SPI0CFG & 0x80) == 1)
	{ }
	P0 &= 0xBF;	// Put 0 to pin P0.6
}

// 
void spi_flash_memory_disable(void)
{
	// Wait until there is not SPI transfer in progress
	while ((SPI0CFG & 0x80) == 1)
	{ }
	P0 |= 0x40;	// Put 1 to pin P0.6
}

// Read a single byte from the flash memory
void flash_read_bytes(Flash_Address addr, unsigned char * bytes, unsigned char numBytes)
{
	readBytesDestination = bytes;
	readBytesRemaining = numBytes;
	spi_flash_memory_enable();		// Set to use flash memory chip
	spi_write_byte(FLASH_COMMAND_READ);	// Write the read byte command
	spi_write_byte(addr.high);			// Write the high addr byte
	spi_write_byte(addr.medium);		// Write the medium addr byte
	spi_write_byte(addr.low);			// Write the low addr byte

	TR1=1;				// Engage the timer for reading bytes

	while (readBytesRemaining)
	{ }
}

// Enable writing to the flash memory
void flash_write_enable(void)
{
	spi_flash_memory_enable();				// Set to use flash memory chip
	spi_write_byte(FLASH_COMMAND_WRITE_ENABLE); // Write the write enable byte
	spi_flash_memory_disable();
}

// Disable writing to the flash memory
void flash_write_disable(void)
{
	spi_flash_memory_enable();				// Set to use flash memory chip
	spi_write_byte(FLASH_COMMAND_WRITE_DISABLE); // Write the write disable byte
	spi_flash_memory_disable();
}


// Write bytes to the memory in AAI mode
// To use this mode you must make the following calls
// 0) flash_erase_ ... either sector or block depending on string size
// 2) flash_write_aai_write(...)
// 3) optional additional flash_write_aai_write(...) calls
void flash_write_bytes(Flash_Address addr, unsigned char * bytes, unsigned char numBytes)
{
	while (flash_get_status() & 0x01)
	{ }

	spi_flash_memory_enable();		// Make sure this info is going to SPI
	flash_write_enable();
	// Send the AAI Write command
	spi_write_byte(FLASH_COMMAND_AAI_PROGRAM);
	// Send the pieces of the address
	spi_write_byte(addr.high);
	spi_write_byte(addr.medium);
	spi_write_byte(addr.low);
	// Send the first byte
	spi_write_byte(*bytes);
	spi_flash_memory_disable();
	
	writeBytesSource = bytes;
	writeBytesRemaining = numBytes-1;
	TMR3CN |= 0x04;		// 
}

unsigned char flash_get_status(void)
{
	unsigned char status;
	spi_flash_memory_enable();
	spi_write_byte(FLASH_COMMAND_READ_STATUS);
	spi_flash_memory_disable();

	TR1 = 1;		// Enable Timer 1

	readBytesDestination = & status;
	readBytesRemaining = 1;

	while (readBytesRemaining)
	{ }

	return status;
}

void flash_erase_sector(Flash_Address addr)
{
	unsigned char status;
	flash_write_enable();
	spi_flash_memory_enable();
	spi_write_byte(FLASH_COMMAND_SECTOR_ERASE);
	spi_write_byte(addr.high);
	spi_write_byte(addr.medium);
	spi_write_byte(addr.low);
	spi_flash_memory_disable();

	do
	{
		status = flash_get_status() & 0x01;
	} while (status);
}

// Read a byte from the SPI
// It assumes there is a byte being recieved
// Lack of a byte being recieved will hang the system indefinately
void spi_read_bytes(void) interrupt 3
{
	
	if (readBytesRemaining==0)
	{
		spi_flash_memory_disable();
		TR1=0;
		return;
	}
	*readBytesDestination = SPI0DAT;
	readBytesDestination++;
	readBytesRemaining--;
}

// Checks every 2 microsec to see if there is a byte to write and another byte to not write
void flash_aai_interrupt_handler(void) interrupt 14
{
	TMR3CN &=0x7F;	// reset high byte flag
	if (flash_get_status() & 0x01)
	{
		if (writeBytesRemaining)
		{
			spi_flash_memory_enable();
			spi_write_byte(FLASH_COMMAND_AAI_PROGRAM);
			spi_write_byte(*writeBytesSource);
			spi_flash_memory_disable();
			
			writeBytesSource++;
			writeBytesRemaining--;
		}
		else
		{
			flash_write_disable();
			TMR3CN &= 0xFB;	// Disable Timer 3
		}
	}
}